# 1.对象
from types import MethodType


class Student1(object):
    def __init__(self, name, score):  # 注意：特殊方法“init”前后有两个下划线！！！
        self.name = name
        self.score = score

    def print_score(self):
        print('%s: %s' % (self.name, self.score))


student1 = Student1("xxx", "afdsfd")
student1.print_score()


# 注意到__init__方法的第一个参数永远是self，表示创建的实例本身，因此，在__init__方法内部，就可以把各种属性绑定到self，因为self就指向创建的实例本身。
# 和普通的函数相比，在类中定义的函数只有一点不同，就是第一个参数永远是实例变量self，并且，调用时，不用传递该参数



print("==============2.私有变量=================")
# 名称前加上两个下划线__
# 名称前加上两个下划线__
# 变量名类似__xxx__的，也就是以双下划线开头，并且以双下划线结尾的，是特殊变量，特殊变量是可以直接访问的，不是private变量，所以，不能用__name__、__score__这样的变量名
# 双下划线开头的实例变量是不是一定不能从外部访问呢？其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name，所以，仍然可以通过_Student__name来访问__name变量
class Student2(object):
    def __init__(self, name, score):
        self.__name = name  # 私有变量 3.6 3.7编译器会将其解释为_Student__name 根据python版本不同解释不同
        self.score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.score))

    def get_name(self):
        print("getname =",self.__name)

student2 = Student2("aaa",33)
print(student2.score)
print(student2._Student2__name) #强行访问私有变量 不建议，建议用get方法
student2._Student2__name = "jiaoseng2" #设置__name的值 不建议 建议用set方法
student2.__name = "jiaoseng1" #不会设置值，只会新增一个变量
student2.get_name()



print("==============3.继承 多态 多继承=================")
# python支持多继承
# 主线都是单一继承下来，把额外增加的功能需要继承的类一般xxxMixIn ，这种设计通常称之为MixIn
# 父类方法重复会使用继承顺序前面的
class Runnable(object):
    def run(self):
        print('Running...')
class BaseA:
    def a1(self):
        print("父类function111")
    def a2(self):
        print("父类function222")

class Aa(BaseA): #继承
    def a1(self):
        print("子类function111")
aa = Aa()
aa.a2()

class Ab(BaseA,Runnable): #多继承
    def a1(self):
        print("子类function111")
aa = Ab()
aa.run()

# 静态语言Vs动态语言
# 对于静态语言（例如Java）来说，如果需要传入Animal类型，则传入的对象必须是Animal类型或者它的子类
# 对于Python这样的动态语言来说，则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了(传递的对象里面有被调用的方法即可
class Timer(object):
    def runaa(self):
        print('传入对象必须有这个方法即可，动态语言特性')

class Timer2(object):
    def fun1(self, obj1):
        obj1.runaa()

Timer2().fun1(Timer())


print("==============4.获取对象信息=================")
a = [22, 33]
type(123)  # 适合判断基本类型
isinstance(a, list)  # 判断对象类型
dir('ABC')  # 获得一个对象的所有属性和方法
# 'ABC'.__len__()    len('ABC')  效果一样



print("============== 5.实例属性和类属性=================")
# 由于Python是动态语言，根据类创建的实例可以任意绑定属性。
def set_score(self):
    print("动态新增的方法")
# 实例对象中动态新增和删除 属性/方法，这种属性是实例对象属性，归该实例对象所有
class Student3(object):
    def __init__(self, name):
        self.name = name
s3 = Student3('Bob')
s3.score = 90  #增加属性
del s3.name    #删除属性
s3.set_score = MethodType(set_score, s3) #增加方法
print(dir(s3))
s3.set_score()

# 类中动态新增方 属性/方法，直接在class中定义属性，这种属性是类属性，归Student类所有
class Student4(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
Student4.sex = '男'  #给类增加属性
Student4.sex2 = 'ee'  #给类增加属性
del Student4.sex2  #给类删除属性 只能删除非构造函数中使用的变量
Student4.set_score = MethodType(set_score, Student4) #给类增加方法

student4 = Student4("xx",16)
print(dir(student4))
student4.set_score()

# __slots__ 用来限制添加属性的
# __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
class Student5(object):
    __slots__ = ("name","age") #允许绑定的变量

Student5.name = "eeeqq"
student5 = Student5()
student5.age = 22
Student5.sex = "nan"
print("限制绑定属性==",dir(student5))
print(student5.name)
print(student5.sex)



print("============== 6.@property(读写)  @xxx.setter（写）=================")
# 把方法变成一个属性
# 设置  @score.setter写  @property读

class Student6(object):
    @property
    def name(self):  #生成property属性时会同时生成一个setter
        return self._namev

    @name.setter #注解的名字和生成property属性的方法名一样score
    def name(self, value):
        self._namev = value

    @property
    def age(self):
        return 2015

st = Student6()
st.name = '方法变属性注解'  # 实际转化为s.set_score('xxxx')
print(st.name)  # 实际转化为s.get_score()
# st.age = 22  因为只设置了property 所以设置值会报错
print(st.age)  # 实际转化为s.get_score()



print("==============7.制定类=================")
class Student3(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print('My name is %s.' % self.name)

    def __str__(self):
        return 'Student object (name: %s)' % self.name

st3 = Student3('zzzzz')

st3()

# 8.枚举
from enum import Enum, unique

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
print(Month(3))
print(Month.Apr.value)


@unique  # 装饰器可以帮助我们检查保证没有重复值。
class Weekday(Enum):
    Sun = 0  # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6


print(Weekday(3))  # 根据V拿K
print(Weekday.Tue)
print(Weekday.Tue.value)  # 根据K拿v

# 9.元类 创建类
print(type(Student3))
print(type(st3))


def fn(self, name='world'):  # 先定义函数
    print('元类:Hello, %s.' % name)


Hello = type('Hello', (object,), dict(hello=fn))  # 创建Hello class

Hello().hello()
